package org.NooLab.glue.net.http.rest;

  
  
import java.io.IOException;
import java.util.Observable;
import java.util.Observer;

import org.NooLab.glue.components.DataReceiverIntf;
import org.NooLab.glue.components.DataSenderIntf;
import org.NooLab.glue.components.pkg.DataDeliveryPackage;
import org.NooLab.glue.net.ComBoxFactory;
import org.NooLab.glue.net.DataSlot;
import org.NooLab.glue.net.UserData; 

 
 
import org.NooLab.utilities.files.DFutils;
import org.NooLab.utilities.logging.PrintLog;
import org.restlet.representation.Representation;


 
/**
 * 
 * This box works completely different as compared to the udpbox or the tcpbox.
 * 
 * The clients do NOT have ANY capability to receive in server-mode, instead, they
 * work like http-clients
 * 
 * The basic challenge for our architecture is to keep the abstraction of the ComBox and the ComBoxFactory,
 * because the client-resource is constructed like this: glueResource = new ClientResource(<url-string>), thus
 * the url-association can NOT be changed after creating the resource.
 * 
 * Hence, we can NOT use different virtual folders for different resources... 
 * 
 * 
 * 
 * 
 * 1. Ports
 * There no particular ports to listen to. In Udp/Tcp, listening is bound to a port.
 * In Restlets, we map this listenport to nothing
 * 
 * Sending is done to the configured port of the http-server
 * 
 * There is no unsolicited incoming message for http-c. Instead, we ONLY get
 * confirmations as an immediate response. Such a response may contain a
 * resource URI for download (get command)
 * 
 * "Listening" thus is comprised by the following steps
 * 
 *   1. POSTing a request for state and/or notification
 *   2. reading the response for resources, response will be in XML
 *   3. connecting with a GET to the offered (dynamic) resource
 *  
 */
public class RestletBox 
							extends 
										Observable 
							implements 
										HttpResponseHandlerIntf, 
										GlueRestletHandlerIntf{
	/*
		the secondary (final) notification will be routed to 
	  	 - MessageBoard,
	  	 - DaraConnector, or
	  	 - MessageReceptor
	   	
	   	yet, the PRIMARY digestion of the response is performed within the RestletBox
	   	
	*/
	// =================================

	// object references ..............
	
	 
	RestBoxDataIntf boxDataIntf ;
	RestBoxData restData ;
	private RestletBoxPropertiesIntf restletBoxProperties;

	ComBoxFactory comboxFactory ;
	
	Observer observer;
	
	// main variables / properties ....
 
	HttpRestletServer serverReceiver;
	
	String clientRole;
	int virtualPort;
	
	// it's not just "restful", they are based on the Restlet framework 
	// private _RestletServer restletServer ;
	// private RestletClient restletClient ;
	 
	
	// constants ......................
	String remoteHostAddress = "localhost";
	int port = 6100;

	int connectionResponseTimeoutValue = 12000 ; // 18000
	// TODO after the first use of the sax parser in registration, we can reduce that !
	int counter=0;
	
	
	// volatile variables .............

	
	
	// helper objects .................
	 
	protected DFutils fileutil = new DFutils() ;
	protected PrintLog out = new PrintLog(2,false);	

	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
	public RestletBox( ComBoxFactory cbfactory , PrintLog outprn){
		
		comboxFactory = cbfactory;
		 
		if (outprn!=null){
			out = outprn;
		}
	}
	// . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

	
	
	public RestBoxDataIntf createRestletDataContainer(){
		
		// boxDataIntf = restdata;
		
		restData = new RestBoxData( this );
		
		return (RestBoxDataIntf) restData;
	}
	
	/**
	 * 
	 * "clientport" will be mapped to a virtual folder like /vp7100 or /vp7200
	 * 
	 * for transports in the local network, "sending" means to write to an output stream;
	 * no direct response to the physical operation is expected.
	 * 
	 * Yet, operations trigger a confirmation, a meaningful response, which would
	 * be sent by the MessageBoard back to the client, and this happens by means of
	 * a separate activity.
	 * it is important, that in our context ALL sending triggers a confirmation 
	 * 
	 * for restlets, the story is different ;
	 * a sending is always something like a short session,
	 * and the confirmation is provided within that session;
	 * 
	 * i.e. the confirmation arrives at the client as a direct result of the
	 * http request
	 * this direct request is taken by a dedicated response handler, which is
	 * maintained by the restlet box, from where it will be routed to the
	 * final operational/semantic processing. 
	 *  
	 * note, that a MessageBoard in the Restlet mode will NEVER actively send!!
	 * TODO: except to another MessageBoard
	 *  
	 * 
	 * @param clientport         will be mapped to a virtual folder in the http-path
	 * @param ctrlport           ...
	 * @param httpCmd            PUT or POST
	 * @param responseDigester   callback pointer
	 * @param description    
	 * @return
	 */
	public DataSenderIntf createSender(  int clientport, int ctrlport, 
										 String description ) {
		HttpRestletSender  sender;
		String virtualFolderBase = "";
		String baseURI ="" ;
		
		// out.print(2, "Restletbox, defining Sender (b)...");
		if (clientport<=0){
			// clientRole;
			clientport = virtualPort ;
		}
		
		virtualFolderBase = "/vp"+clientport;
		
		baseURI = "http://" + remoteHostAddress +":"+ port + virtualFolderBase;
		
		sender = new HttpRestletSender( baseURI, "",
										(HttpResponseHandlerIntf)this ,out); // the callback address for primary digestions
		
		
		return sender;
	}



	/**
	 * 
	 * "Listening" is comprised by the following steps
	 * 
	 *   1. POSTing a request for state and/or notification
 	 *   2. reading the response for resources, response will be in XML
 	 *   3. connecting with a GET to the offered (dynamic) resource* 
	 * 
	 *   running a thread which is repeating those steps 
	 *   
	 * @param listenport
	 * @param ctrlport
	 * @param observer
	 * @param description
	 * @return
	 */
	public DataReceiverIntf createReceiver( int listenport, int ctrlport, Observer observer, String description) {
	
		//as e.g. for :  udpReceiver = new UdpDataReceiver( listenport , ctrlport, observer, description );
		HttpRestletReceiver receiver;
		
		String virtualFolderBase = "";
		String baseURI ="" ;
		
		// out.print(2, "Restletbox, defining Receiver (c)...");
		
		clientRole = comboxFactory.getRole() ;
		if (clientRole.toLowerCase().contains("source")){
			virtualPort = comboxFactory.getSourceListenPort() ;
		}else{
			virtualPort = comboxFactory.getReceptorListenPort() ;
		}
		
		// listenport is the virtual port for this role on the server
		virtualFolderBase = "/vp"+virtualPort; 
		port = listenport;
		
		
		// . . . . . . . . . . . . . . . . .
		
		baseURI = "http://" + remoteHostAddress +":"+ port + virtualFolderBase;
		
		// MessageBoardRestletServer
		// this creates a server component, based on the settings passed through from the ComBoxFactory
		// (GlueRestletHandlerIntf)this
		// baseurl is vp7070 which is nonsense
		receiver = new HttpRestletReceiver( baseURI, clientRole ,(HttpResponseHandlerIntf)this, description);
		// put it to a collection ? or is there only one?
		
		
		
		return receiver;
	}

	/**
	 * 
	 * this creates an instance of a http-restlet receiver for the MessageBoard;
	 * it is a pure receiver listener, both logically and physically !!
	 * 
	 * @param restletBoxProps
	 * @return
	 */
	public DataReceiverIntf createServerAsReceiver( RestletBoxPropertiesIntf restletBoxProps, 
													Observer observer ){
		
		serverReceiver = null;
		String baseURI ="" ;

		restletBoxProperties = restletBoxProps ;
		
		port = restletBoxProperties.getListeningPort();
		remoteHostAddress = restletBoxProperties.getServerAddress() ; 
		
		baseURI = "http://" + remoteHostAddress +":"+ port ;
		
		
		serverReceiver = new HttpRestletServer( restletBoxProperties , observer ); // still the messageboard
		
		if (serverReceiver.status <0){
			serverReceiver = null ;
			this.observer = null ;
		}else{
			this.observer = observer ; // TODO notification ...
			serverReceiver.setConnectionResponseTimeoutValue( connectionResponseTimeoutValue ); 
			//out.print(2, "Restletbox, defining serverReceiver (a)...");
		}
		
		return serverReceiver;
	}
	
	/**
	// we also create a pseudo sender...
	// -> obeys to the sender interface
	// -> internally, it is just linking to and updating the waiting instance, which
	//    takes it to create an immediate response...
	
	// within the creation process, the Observer linkage will be established;
	// such, from the outside (outside of the Restlet framework), it looks like sending,
	// yet, it is not...
	
	// sending must put the message also into a queue, if insertion into the 
	// response is not successful !!! 
	// -> the response must remove the item from this queue
	 * 
	 * 
	 * @param internalVport
	 * @param ctrlport
	 * @param description
	 * @return
	 */
	public DataSenderIntf createInternalSender( int internalVport, int ctrlport, String description) {
		
		HttpRestletInternalSender  sender = null;
		String virtualFolderBase = "";
		String baseURI ="" ;
		
		if (internalVport<=0){
			// clientRole;
			internalVport = virtualPort ;
		}
		
		virtualFolderBase = "/vp"+internalVport;
		
		// baseURI = "http://" + hostaddress +":"+ port + virtualFolderBase;
		
		// sender = new HttpRestletSender( baseURI, "", responseDigester ,out); // the callback address for primary digestions
		// not this here!! 

		// creating a specialized object which refers to "serverReceiver" !
		// and which implements DataSenderIntf
		sender = new HttpRestletInternalSender( serverReceiver );  
		
		return sender;
	}
	
	
	
	public void initialize( RestletBoxPropertiesIntf rprops){
		UserData boxUser = new UserData();
		 
		boxUser.setName( rprops.getBoxUserName() );
		boxUser.setPassword( rprops.getBoxUserPwd() ) ;
		boxUser.setHomeDir( rprops.getBoxUserDir() ) ;
		
		if (rprops.getLoggingLevelAlias()>=2){
			out = new PrintLog(2,true);
		}
		
		
		
		out.print(2, "creating instance of Restlet-Server ...  ") ;
		/*
		    a server is just created if the calling instance is the MessageBoard !
		    yet, such a construction will happen in createReceiver
		     
		restletServer = new RestletServer( rprops.getListeningPorts()[0], 
				   					   	   (GlueRestletHandlerIntf)( rprops.getRestDataEndpoint() ), 
				   					   	   boxUser,
				   					   	   rprops.getLoggingLevelAlias() ) ;
		 */

		out.print(2, "creating instance of Restlet-Client ... ") ;

		// restletClient = new RestletClient( boxUser, rprops.getLoggingLevelAlias() );
		// restletClient.setup() ;
	}

	  
	public RestletBoxPropertiesIntf createRestBoxProperties() {
	 
		DataDeliveryPackage restDeliveryPkg = null;
		
		restletBoxProperties = new RestletBoxProperties();
		
		restletBoxProperties.setRestDataEndpoint( this );
		
		restDeliveryPkg = new DataDeliveryPackage();
		
		
		restletBoxProperties.setRestDeliveryPackage(restDeliveryPkg)  ; 
			
			
		return (RestletBoxPropertiesIntf)restletBoxProperties ;
	}

	
	public void startup( ){
		// restletServer.start();
	}
	
	public void stop(){
		// restletServer.stop() ;
	}
	
	public void setObserver( Observer obsrvInstance){
		observer = obsrvInstance ;
		if (obsrvInstance!=null){
			addObserver( obsrvInstance );
		}else{
			out.print(2, "refresh observer for RestleBox!");
		}
	}
	
	
	
	
	public String getRemoteHostAddress() {
		return remoteHostAddress;
	}
	public void setRemoteHostAddress( String address ) {
		String str="";
		int p;
		
		address=address.trim();
		p=address.indexOf(":");
		if (p>0){
			str = address.substring(p+1, address.length());
			str=str.replace(":", ""); str=str.replace("/", "").trim();
			address = address.substring(0,p);
			if (str.length()>0){
				port = Integer.parseInt(str) ;
			}
		}
		remoteHostAddress = address;
	}


	@Override
	public void getRestletMessageStr(String msg) {
		
	}

	@Override
	public void getRestletMessageObj(DataSlot dataslot) {
		
	}
	
	// -----------------------------------------------------------------------
	// interface HttpResponseHandlerIntf
	@Override
	public void setHttpResponse(String roleOfReceiver, String responseStr) {
		
		// rBox.out.print(2, "Role : "+ rBox.clientRole+" -> RestletBox :: ResponseDigester :: setHttpResponse(1) \n" + responseStr) ;
	}
	@Override
	public void setHttpResponse(String roleOfReceiver, Representation responseRepr) {
		String str;
		
		
		try {
			
			str = responseRepr.getText();
if ((str!=null) && (str.contains("WXY"))){
	str = str+"" ;
}
			// setHttpResponse( roleOfReceiver, str ) ;
			if (str!=null){
				str = " \n\r"+str;
			}else{ 
				str="" ;
			}
												out.print(5, "Relaying HttpResponse in RestletBox to "+countObservers()+
															 " observers, role : "+ clientRole) ;  
			
			// should flow back to the Observer, either DataConnector (Source), or MessageReceptor (Receptor)
			// 
			String _str = "null";
			if (observer!=null){
				_str = observer.toString();
			}
			
												out.print(5, "Observer is : "+ _str) ;
			
			if (observer==null){
				
			} 
			setChanged();
				
			notifyObservers(str) ;
			
					
					
		} catch (IOException e) {
			 
			e.printStackTrace();
		}
		
		
	}



	public int getConnectionResponseTimeoutValue() {
		return connectionResponseTimeoutValue;
	}



	public void setConnectionResponseTimeoutValue(int connectionResponseTimeoutValue) {
		this.connectionResponseTimeoutValue = connectionResponseTimeoutValue;
		
		if (serverReceiver!=null){
			serverReceiver.setConnectionResponseTimeoutValue( connectionResponseTimeoutValue );
		}
	}
	
	// -----------------------------------------------------------------------

	
	
	
}


